from cmdb.models import Idc, ServerGroup, Server
from cmdb.serializers import IdcSerializers, ServerGroupSerializers, ServerSerializers
from rest_framework.viewsets import ModelViewSet
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.permissions import IsAuthenticated


class IdcViewSet(ModelViewSet):
    queryset = Idc.objects.all()
    serializer_class = IdcSerializers
    filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend]
    search_fields = ('name', 'city', 'provider')  # 指定可搜索的字段
    filterset_fields = ('name', 'city',)  # 指定可过滤的字段
    # 排序
    # 注意 filter_backends多了一个filters.OrderingFilter
    ordering_fields = ["id", "name"]

    # 认证&权限配置
    # authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]

    # 重写删除方法
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        try:
            self.perform_destroy(instance)
            res = {'code': 200, 'msg': '删除成功'}
        except Exception as e:
            res = {'code': 500, 'msg': '该IDC机房管理关联其他应用，请删除关联的应用再操作'}
        return Response(res)

    # 重写更新方法
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        # res = {'code': 500, 'msg': '主机配置信息同步失败，错误信息： %s' % result['msg']}
        res = {'code': 200, 'msg': '修改成功'}
        return Response(res)

    # 重写创建方法
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            self.perform_create(serializer)
            res = {'code': 200, 'msg': '创建idc成功'}
        except Exception as e:
            res = {'code': 500, 'msg': '创建idc失败%s' % e}
        return Response(res)


class ServerGroupViewSet(ModelViewSet):
    queryset = ServerGroup.objects.all()
    serializer_class = ServerGroupSerializers
    filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend]
    filterset_fields = ('name',)  # 指定可过滤的字段
    search_fields = ('name',)  # 指定可搜索的字段
    # 认证&权限配置
    # authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]
    # 排序
    # 注意 filter_backends多了一个filters.OrderingFilter
    ordering_fields = ["id", "name"]

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        try:
            self.perform_destroy(instance)
            res = {'code': 200, 'msg': '删除成功'}
        except Exception as e:
            res = {'code': 500, 'msg': '该主机分组关联其他应用，请删除关联的应用再操作'}
        return Response(res)


class ServerViewSet(ModelViewSet):
    queryset = Server.objects.all()
    serializer_class = ServerSerializers
    filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend]
    search_fields = ("hostname",)
    filterset_fields = ("hostname",)
    ordering_fields = ("id",)
    # 认证&权限配置
    # authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]


from rest_framework.views import APIView
import os
from django.conf import settings
import json
from rest_framework.response import Response
from system_config.models import Credential
from libs.ssh import SSH
from django.db import IntegrityError


class CreateHostView(APIView):
    def post(self, request):

        print(request.data)
        idc_id = int(request.data.get('idc'))  # 机房id
        server_group_id_list = request.data.get('server_group')  # 分组id
        name = request.data.get('name')
        hostname = request.data.get('hostname')
        ssh_ip = request.data.get('ssh_ip')
        ssh_port = int(request.data.get('ssh_port'))
        credential_id = int(request.data.get('credential'))
        note = request.data.get('note')
        # 通过凭据ID获取用户名信息
        credential = Credential.objects.get(id=credential_id)
        username = credential.username

        if credential.auth_mode == 1:
            password = credential.password
            ssh = SSH(ssh_ip, ssh_port, username, password)
        else:
            private_key = credential.private_key
            ssh = SSH(ssh_ip, ssh_port, username, key=private_key)

        # 测试ssh链接是否成功
        result = ssh.test()
        if result['code'] == 200:
            local_file = os.path.join(settings.BASE_DIR, 'cmdb', 'files', 'local_host_collect_linux.py')
            remote_file = os.path.join('/tmp/local_host_collect_linux.py')
            ssh.scp(local_file, remote_file)
            result = ssh.command('python3 %s' % remote_file)
            # 服务器信息采集成功，入库
            if result['code'] == 200:
                idc = Idc.objects.get(id=idc_id)
                try:
                    server_obj = Server.objects.create(
                        idc=idc,
                        name=name,
                        credential=credential,
                        hostname=hostname,
                        ssh_ip=ssh_ip,
                        ssh_port=ssh_port,
                        is_verified='verified',
                        note=note
                    )
                    print(server_obj)
                    for group_id in server_group_id_list:
                        group = ServerGroup.objects.get(id=group_id)
                        server_obj.server_group.add(group)
                    # 服务器配置信息入库
                    data = json.loads(result['data'])
                    Server.objects.filter(hostname=hostname).update(**data)
                    res = {'code': 200, 'msg': '添加主机成功，并同步配置'}
                except IntegrityError as e:
                    res = {'code': 500, 'msg': '主机已经存在'}
            else:
                res = {'code': 500, 'msg': '主机配置信息同步失败，错误信息： %s' % result['msg']}
        else:
            res = {'code': 500, 'msg': '错误信息： %s' % result['msg']}

        return Response(res)


# Excel创建主机接口
from django.http import FileResponse
import xlrd


class ExcelCreateHostView(APIView):
    # 下载主机导入模板.xlsx
    def get(self, request):
        file_name = 'local_server_host.xlsx'
        file_path = os.path.join(settings.BASE_DIR, 'cmdb', 'files', file_name)
        response = FileResponse(open(file_path, 'rb'))
        response['Content-Type'] = 'application/octet-stream'
        response['Content-Disposition'] = 'attachment;filename=%s' % file_name
        return response

    # 导入excel
    def post(self, request, *args, **kwargs):
        # 获取前端提交数据
        excel_file_obj = request.data['file']
        idc_id = int(request.data.get('idc'))
        server_group_id = request.data.get('server_group')

        # 直接自定义分隔符，把字符串转换为列表格式
        server_group_list_id = server_group_id.split(',')

        # 判断读取是否是excel文件
        try:
            data = xlrd.open_workbook(file_contents=excel_file_obj.read(), filename=None)
        except Exception as e:
            result = {'code': 500, 'msg': '请上传Excel文件！'}
            return Response(result)

        idc = Idc.objects.get(id=idc_id)
        # 打开第一个工作表
        table = data.sheets()[0]
        # 获取表的行数
        nrows = table.nrows
        try:
            for i in range(nrows):
                if i != 0:  # 跳过标题行
                    name = table.row_values(i)[0]
                    hostname = table.row_values(i)[1]
                    ssh_ip = table.row_values(i)[2]
                    ssh_port = table.row_values(i)[3]
                    note = table.row_values(i)[4]
                    server = Server.objects.create(
                        idc=idc,
                        name=name,
                        hostname=hostname,
                        ssh_ip=ssh_ip,
                        ssh_port=ssh_port,
                        note=note
                    )
                    # 添加多对多字段
                    for group_id in server_group_list_id:
                        group = ServerGroup.objects.get(id=group_id)  # 获取分组
                        server.server_group.add(group)  # 将服务器添加到分组
            result = {'code': 200, 'msg': 'excel导入主机成功'}
        except Exception as e:
            result = {'code': 500, 'msg': 'excel导入主机异常%s' % e}
        return Response(result)


from libs.aliyun_cloud import AliCloud
import time


class AliyunCloudView(APIView):
    def get(self, request):
        # 获取region信息
        secret_id = request.query_params.get('secret_id')
        secret_key = request.query_params.get('secret_key')
        cloud = AliCloud(secret_id, secret_key)
        region_result = cloud.region_list()
        code = region_result['code']

        if code == 200:
            # 二次处理，固定字段名
            region = []
            for r in region_result['data']['Regions']['Region']:
                region.append({"region_id": r['RegionId'], 'region_name': r['LocalName']})
            res = {'code': 200, 'msg': '获取区域列表成功', 'data': region}
        else:
            res = {'code': 500, 'msg': '获取区域列表成功失败:%s' % region_result['msg']}
        return Response(res)

    def post(self, request, *args, **kwargs):
        """
        根据区域名称创建机房，再导入云主机（绑定机房）到数据库
        """
        # 凭据、IDC机房、主机分组、SSH连接地址（IP、端口）
        secret_id = request.data.get('secret_id')
        secret_key = request.data.get('secret_key')
        server_group_id = int(request.data.get('server_group'))
        region_id = request.data.get('region')  # 区域用于机房里的城市
        ssh_ip = request.data.get('ssh_ip')  # 用户选择使用内网（private）还是公网（public），下面判断对应录入
        ssh_port = int(request.data.get('ssh_port'))

        cloud = AliCloud(secret_id, secret_key)
        instance_result = cloud.instance_list(region_id)

        instance_list = []
        if instance_result['code'] == 200:
            instance_list = instance_result['data']['Instances']['Instance']
            if len(instance_list) == 0:
                res = {'code': 500, 'msg': '该区域未发现云主机，请重新选择！'}
                return Response(res)
        elif instance_result['code'] == 500:
            res = {'code': 500, 'msg': '%s' % instance_result['msg']}
            return Response(res)

        # InstanceSet中可用区字段值是英文，例如 ap-beijing-1
        # 先获取可用区英文与中文对应，下面遍历主机再获取中文名
        zone_result = cloud.zone_list(region_id)
        zone_dict = {}
        for z in zone_result['data']['Zones']['Zone']:
            zone_dict[z['ZoneId']] = z['LocalName']

        # 获取主机所在可用区
        # 可用区用于机房里的机房名称
        zone_set = set()
        for host in instance_list:
            zone = host['ZoneId']  # 可用区，例如 ap-beijing-1
            zone_set.add(zone_dict[zone])  # 获取中文名

        # 根据可用区创建机房
        for zone in zone_set:
            # 如果存在不创建
            idc = Idc.objects.filter(name=zone)
            if not idc:
                city = ""
                region_list = cloud.region_list()['data']['Regions']['Region']
                for r in region_list:  # 获取区域对应中文名
                    if r['RegionId'] == region_id:
                        city = r['LocalName']
                Idc.objects.create(
                    name=zone,
                    city=city,
                    provider="阿里云"
                )

        # 导入云主机信息到数据库
        for host in instance_list:
            zone = host['ZoneId']
            instance_id = host['InstanceId']  # 实例ID
            # hostname = host['HostName']
            instance_name = host['InstanceName']  # 机器名称
            os_version = host['OSName']

            private_ip_list = host['NetworkInterfaces']['NetworkInterface'][0]['PrivateIpSets']['PrivateIpSet']
            private_ip = []
            for ip in private_ip_list:
                private_ip.append(ip['PrivateIpAddress'])

            public_ip = host['PublicIpAddress']['IpAddress']
            cpu = "%s核" % host['Cpu']
            memory = "%sG" % (int(host['Memory']) / 1024)

            # 硬盘信息需要单独获取
            disk = []
            disk_list = cloud.instance_disk(instance_id)['data']['Disks']['Disk']
            for d in disk_list:
                disk.append({'device': d['Device'], 'size': '%sG' % d['Size'], 'type': None})

            create_date = time.strftime("%Y-%m-%d", time.strptime(host['CreationTime'], "%Y-%m-%dT%H:%MZ"))
            # 2022-01-30T04:51Z 需要转换才能存储
            expired_time = time.strftime("%Y-%m-%d %H:%M:%S", time.strptime(host['ExpiredTime'], "%Y-%m-%dT%H:%MZ"))

            # 创建服务器
            idc_name = zone_dict[zone]
            idc = Idc.objects.get(name=idc_name)  # 一对多

            if ssh_ip == "public":
                ssh_ip = public_ip[0]
            elif ssh_ip == "private":
                ssh_ip = private_ip[0]

            data = {'idc': idc,
                    'name': instance_name,
                    'hostname': instance_id,
                    'ssh_ip': ssh_ip,
                    'ssh_port': ssh_port,
                    'machine_type': 'cloud_vm',
                    'os_version': os_version,
                    'public_ip': public_ip,
                    'private_ip': private_ip,
                    'cpu_num': cpu,
                    'memory': memory,
                    'disk': disk,
                    'put_shelves_date': create_date,
                    'expire_datetime': expired_time,
                    'is_verified': 'verified'}
            # 如果instance_id不存在才创建
            server = Server.objects.filter(hostname=instance_id)
            if not server:
                server = Server.objects.create(**data)
                # 分组多对多
                group = ServerGroup.objects.get(id=server_group_id)  # 根据id查询分组
                server.server_group.add(group)  # 将服务器添加到分组
            else:
                server.update(**data)

        res = {'code': 200, 'msg': '导入云主机成功'}
        return Response(res)


from libs.tencent_cloud import TCloud


class TencentCloudView(APIView):
    """
    腾讯云获取云主机信息
    """

    def get(self, request):
        """
        返回所有区域
        """
        secret_id = request.query_params.get('secret_id')
        secret_key = request.query_params.get('secret_key')

        # cloud = TencentCloud("AKIDKUP3QWNxGI4ZnkQpwnMgiTxA7mAH8i02", "nJYWDtJSKwGJ5aokPownMegRu61f27wU")
        cloud = TCloud(secret_id, secret_key)
        region_result = cloud.region_list()

        code = region_result.code
        if code == 200:
            # 二次处理，固定字段名，与阿里云的一致
            region = []
            for r in region_result.RegionSet:
                region.append({"region_id": r.Region, 'region_name': r.RegionName})
            res = {'code': code, 'msg': '获取区域列表成功！', 'data': region}
        else:
            res = {'code': code, 'msg': region_result.msg}
        return Response(res)

    def post(self, request, *args, **kwargs):
        """
        根据区域名称创建机房，再导入云主机（关联机房）到数据库
        """
        secret_id = request.data.get('secret_id')
        secret_key = request.data.get('secret_key')
        server_group_id = int(request.data.get('server_group'))
        region_id = request.data.get('region')
        ssh_ip = request.data.get('ssh_ip')
        ssh_port = int(request.data.get('ssh_port'))

        cloud = TCloud(secret_id, secret_key)
        instance_result = cloud.instance_list(region_id)

        instance_list = []
        if instance_result.code == 200:
            instance_list = instance_result.InstanceSet
            if instance_result.TotalCount == 0:
                res = {'code': 500, 'msg': '该区域未发现云主机，请重新选择！'}
                return Response(res)
        elif instance_result.code == 500:
            res = {'code': 500, 'msg': '%s' % instance_result['msg']}
            return Response(res)

        # InstanceSet中可用区字段值是英文
        # 先获取可用区英文与中文对应，下面遍历主机再获取中文名
        zone_result = cloud.zone_list(region_id)
        zone_dict = {}
        for z in zone_result.ZoneSet:
            zone_dict[z.Zone] = z.ZoneName

        # 获取主机所在可用区
        # 可用区，机房表：机房名称
        host_zone_set = set()
        for host in instance_list:
            zone = host.Placement.Zone  # 可用区，例如 ap-beijing-1
            host_zone_set.add(zone_dict[zone])  # 获取中文名

        # 根据可用区创建机房
        for zone in host_zone_set:
            # 如果存在不创建
            idc = Idc.objects.filter(name=zone)
            if not idc:
                city = ""
                region_result = cloud.region_list()
                for r in region_result.RegionSet:  # 获取区域对应中文名
                    if r.Region == region_id:
                        city = r.RegionName

                Idc.objects.create(
                    name=zone,
                    city=city,
                    provider="腾讯云"
                )

        # 导入云主机信息到数据库
        for host in instance_list:
            zone = host.Placement.Zone
            instance_id = host.InstanceId  # 实例ID
            instance_name = host.InstanceName  # 机器名称

            os_version = host.OsName
            private_ip = host.PrivateIpAddresses
            public_ip = host.PublicIpAddresses
            cpu = "%s核" % host.CPU
            memory = "%sG" % host.Memory

            disk = [{'device': 'None', 'size': host.SystemDisk.DiskSize, 'type': 'None'}]  # 默认保存是系统盘
            data_list = host.DataDisks
            if data_list:
                for d in data_list:
                    disk.append({'device': 'None', 'size': d.DiskSize, 'type': 'None'})

            create_date = time.strftime("%Y-%m-%d", time.strptime(host.CreatedTime, "%Y-%m-%dT%H:%M:%SZ"))
            expired_time = time.strftime("%Y-%m-%d %H:%M:%S", time.strptime(host.ExpiredTime, "%Y-%m-%dT%H:%M:%SZ"))
            # state = host.InstanceState

            # 创建服务器
            idc_name = zone_dict[zone]
            idc = Idc.objects.get(name=idc_name)

            if ssh_ip == "public":
                ssh_ip = public_ip[0]  # 使用第一个IP连接
            elif ssh_ip == "private":
                ssh_ip = private_ip[0]

            # 如果instance_id不存在才创建
            data = {'idc': idc,
                    'name': instance_name,
                    'hostname': instance_id,
                    'ssh_ip': ssh_ip,
                    'ssh_port': ssh_port,
                    'machine_type': 'cloud_vm',
                    'os_version': os_version,
                    'public_ip': public_ip,
                    'private_ip': private_ip,
                    'cpu_num': cpu,
                    'memory': memory,
                    'disk': disk,
                    'put_shelves_date': create_date,
                    'expire_datetime': expired_time,
                    'is_verified': 'verified'}
            server = Server.objects.filter(hostname=instance_id)
            if not server:
                server = Server.objects.create(**data)
                # 分组多对多
                group = ServerGroup.objects.get(id=server_group_id)  # 根据id查询分组
                server.server_group.add(group)  # 将服务器添加到分组
            else:
                server.update(**data)

        res = {'code': 200, 'msg': '导入云主机成功'}
        return Response(res)


...


class HostCollectView(APIView):
    def get(self, request):
        hostname = request.query_params.get('hostname')
        server = Server.objects.get(hostname=hostname)
        ssh_ip = server.ssh_ip
        ssh_port = server.ssh_port

        # 未绑定凭据并且没有选择凭据
        credential_id = request.query_params.get('credential_id')
        if not server.credential and not credential_id:
            result = {'code': 500, 'msg': '未发现凭据，请选择！'}
            return Response(result)
        elif server.credential:
            credential_id = int(server.credential.id)
        elif credential_id:
            credential_id = int(request.query_params.get('credential_id'))

        credential = Credential.objects.get(id=credential_id)

        username = credential.username
        if credential.auth_mode == 1:
            password = credential.password
            ssh = SSH(ssh_ip, ssh_port, username, password=password)
        else:
            private_key = credential.private_key
            ssh = SSH(ssh_ip, ssh_port, username, key=private_key)

        # 先SSH基本测试
        test = ssh.test()
        if test['code'] == 200:
            local_file = os.path.join(settings.BASE_DIR, 'cmdb', 'files', 'cloud_host_collect_linux.py')
            remote_file = os.path.join('/tmp/cloud_host_collect_linux.py')
            ssh.scp(local_file, remote_file)
            result = ssh.command('python %s' % remote_file)

            if result['code'] == 200:  # 采集脚本执行成功
                # 再进一步判断客户端采集脚本提交结果

                data = json.loads(result['data'])
                Server.objects.filter(hostname=hostname).update(**data)

                # 更新凭据ID
                server = Server.objects.get(hostname=hostname)
                server.credential = credential
                server.is_verified = 'verified'
                server.save()

                result = {'code': 200, 'msg': '主机配置同步成功'}

            else:
                result = {'code': 500, 'msg': '主机配置同步失败！错误：%s' % result['msg']}
        else:
            result = {'code': 500, 'msg': 'SSH连接异常！错误：%s' % test['msg']}
        return Response(result)
